#include "base/core/char_operation.h"
#include <errno.h>
#include <string.h>
#include <sstream>
#include <stdarg.h> 
#include "base/core/com_define.h"
#include "base/core/os/debug.h"
#include "base/core/utility.h"
#include "base/core/os/string.h"

char Value2Ascii(char in) 
{
	MMO_ASSERT(in >= 0);
	MMO_ASSERT(in <= 15);

	if (in <= 9)
		return '0' + in;
	return 'A' + (in - 10);
}

char Ascii2Value(char in) 
{
	if (in >= '0' && in <= '9')
		return in - '0';

	if (in >= 'A' && in <= 'F')
		return in - 'A' + 10;

	if (in >= 'a' && in <= 'f')
		return in - 'a' + 10;

	MMO_ASSERT(0);
	return 0;
}

bool StringValidCheck(const char * buf, size_t buf_len) 
{
	MMO_ASSERT_ERROR(buf, false, EINVAL);
	MMO_ASSERT_ERROR(buf_len > 0, false, EINVAL);

	for (size_t i = 0; i < buf_len; ++i) 
	{
		switch (buf[i]) 
		{
		case '\0': {
			return true;
		}
			break;
		case '\'':
		case '\"':
		case ')':
		case '(':
		case '=':
		case '%': {
			return false;
		}
			break;
		default:
			break;
		}
	}
	return true;
}

size_t Binary2String(const char * src, size_t src_len, char * des, size_t des_len) 
{
	MMO_ASSERT_ERROR(src, false, EINVAL);
	MMO_ASSERT_ERROR(src_len > 0, false, EINVAL);
	MMO_ASSERT_ERROR(des_len > 0, false, EINVAL);
	MMO_ASSERT_ERROR(des, false, EINVAL);

	unsigned int des_pos = 0;
	size_t len = TMIN(src_len, des_len / 2);
	for (size_t i = 0; i < len; ++i) 
	{
		des[des_pos] = Value2Ascii(((unsigned char)src[i]&0xF0)>>4);
		des_pos++;
		des[des_pos] = Value2Ascii(src[i]&0x0F);
		des_pos++;
	}
	return len * 2;
}

size_t DBstr2Binary(const char * src, size_t src_len, char * des, size_t des_size) 
{
	MMO_ASSERT_ERROR(src, false, EINVAL);
	MMO_ASSERT_ERROR(src_len > 0, false, EINVAL);
	MMO_ASSERT_ERROR(des, false, EINVAL);
	MMO_ASSERT_ERROR(des_size > 0, false, EINVAL);

	size_t des_pos = 0;
	for (size_t i = 0; i < (src_len - 1); ) 
	{
		if (src[i] == '\0' || src[i+1] == '\0')
			break;

		des[des_pos] = (Ascii2Value(src[i]) << 4) + Ascii2Value(src[i+1]);
		++des_pos;
		i += 2;

		if (des_pos >= des_size)
			break;
	}
	return des_pos;
}


bool SafeCopyString(char* dest, size_t destSize, const char* source)
{
    bool result = false;
    size_t strLength = 0;

    MMOLOG_ASSERT_EXIT(dest);
    MMOLOG_ASSERT_EXIT(source);

    strLength = strlen(source);
    MMOLOG_ASSERT_EXIT(strLength < destSize);

    memcpy(dest, source, strLength + 1);

    result = true;
Exit0:
    return result;
}

bool LowerCaseString(char* str, size_t len) {
    MMO_ASSERT(str != nullptr);

    while (*str != '\0' && len-- > 0) 
	{
        *str = (char)tolower((int)(*str));
        str++;
    }
    return true;
}

bool UpCaseString(char* str, size_t len) 
{
    MMO_ASSERT(str != nullptr);
    while (*str != '\0' && len-- > 0) 
	{
        *str = (char)toupper((int)(*str));
        str++;
    }
    return true;
}

std::vector<std::string> &StringSplit(const std::string &s, char delim, std::vector<std::string> &elems) 
{
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) 
	{
        elems.push_back(item);
    }
    return elems;
}
std::vector<int> &StringSplit2Int(const std::string &s, char delim, std::vector<int> &elems)
{
	std::stringstream ss(s);
	std::string item;
	int32_t sValue = 0;
	while (std::getline(ss, item, delim))
	{
		std::istringstream iss(item);
		iss >> sValue;
		elems.push_back(sValue);
	}
	return elems;
}

bool StringSplit2Uid(const std::string &s, uint32_t &uid)
{
	std::stringstream ss(s);
	std::string item;
	std::vector<std::string> elems;
	while (std::getline(ss, item, '_'))
	{
		elems.push_back(item);
	}
	if (elems.size() != 2)
		return false;

	if (elems[0] != "uid")
		return false;

	std::istringstream iss(item);
	iss >> uid;

	return true;
}

int utf8_snprintf(char* dest, size_t destSize, const char* format, ...) 
{
    MMO_ASSERT(dest != nullptr);
    MMO_ASSERT(destSize > 0);
    MMO_ASSERT(format != nullptr);

    int readLen = 0;
    int index   = 0;
    int len     = 0;
    char* end = &dest[destSize - 1];

    va_list vlist;
    va_start(vlist, format);

    while (&dest[readLen] < end && format[index] != '\0') 
	{
        unsigned char ch = format[index];
        unsigned char ch1 = format[index + 1];
        if (ch == '%' && ch1 != '\0') 
		{
            switch (ch1) 
			{
            case 'd':
                len = snprintf(&dest[readLen], destSize - readLen, "%d", (int32_t)va_arg(vlist, int32_t));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case 'u':
                len = snprintf(&dest[readLen], destSize - readLen, "%u", (uint32_t)va_arg(vlist, uint32_t));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case 'f':
                len = snprintf(&dest[readLen], destSize - readLen, "%f", (float)va_arg(vlist, double));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case 'e':
                len = snprintf(&dest[readLen], destSize - readLen, "%e", (double)va_arg(vlist, double));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case 'p':
                len = snprintf(&dest[readLen], destSize - readLen, "%p", (void*)va_arg(vlist, void*));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case 'l':
            {
                unsigned char ch2 = format[index + 2];
                if (ch2 == 'l') {
                    unsigned char ch3 = format[index + 3];
                    if (ch3 == 'd') {
                        len = snprintf(&dest[readLen], destSize - readLen, "%lld", (long long)va_arg(vlist, long long));
                        MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                        index += 4;
                        readLen += len;
                        continue;
                    }
                    else if (ch3 == 'u') {
                        len = snprintf(&dest[readLen], destSize - readLen, "%llu", (unsigned long long)va_arg(vlist, unsigned long long));
                        MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                        index += 4;
                        readLen += len;
                        continue;
                    }
                }
                else if (ch2 == 'f') {
                    len = snprintf(&dest[readLen], destSize - readLen, "%lf", (double)va_arg(vlist, double));
                    MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                    index += 3;
                    readLen += len;
                    continue;
                }

                LogErrFmtPrint("utf8_snprintf unsupport %s", format);
                goto Exit0;
                break;
            }
            case 's':
                len = snprintf(&dest[readLen], destSize - readLen, "%s", (const char*)va_arg(vlist, const char*));
                MMOLOG_PROCESS_ERROR(len > 0 && len < (int)destSize - readLen);
                index += 2;
                readLen += len;
                continue;
                break;
            case '%':
                index += 2;
                dest[readLen++] = '%';
                continue;
                break;
            default:
                LogErrFmtPrint("utf8_snprintf unsupport %s", format);
                goto Exit0;
                break;
            }
        }
        dest[readLen++] = format[index++];
    }

Exit0:
    dest[readLen] = '\0';
    va_end(vlist);
    return readLen;
}
